import random
from sbs_shared import *

#===============================================================================
# ServerBase
#===============================================================================

class ServerBase:
    
    def __init__(self):
        self.host = None
        
        # allow subclasses to register message handlers. 
        # See register_message_handler()
        self._message_handler = {}
        
        self.verbose = True
        
    def register_message_handler(self, message, handler_fn):
        if self.verbose:
            print "register_message_handler. message:", message, "handler_fn:", handler_fn
        #if (handler_fn == None):
        if not handler_fn:
            raise Exception("handler_fn == None")
        if (message == ''):
            raise Exception("message == ''")
        self._message_handler[message] = handler_fn
        
    def on_connect(self, addr):
        if self.verbose:
            print "[ServerBase] Got a connection from", addr
    
    def on_bye(self, addr):
        if self.verbose:
            print "[ServerBase] Bye from", addr
            
    def on_connection_lost(self, addr): 
        if self.verbose:
            print "[ServerBase] Lost connection to", addr
    
    def on_server_full(self):
        if self.verbose:
            print "[ServerBase] No more clients are accepted right now"
    
#===============================================================================
# GameServer - Subclass this to create a game server
#===============================================================================

class GameServer(ServerBase):
    
    def __init__(self):      
        ServerBase.__init__(self)  
        
        self.port = None
        self.__open_sockets = []
        self.__open_sockets_to_addr = {}
        self.__addr_to_open_socket = {}
        self.__listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__listen_socket.setblocking(False)
        
        self.register_message_handler("bye", self.on_bye)
        
        # Sequence numbers from the clients are even numbers.
        # Sequence numbers from the server are odd numbers.
        self.__seq_no = SBS_SERVER_SEQ_NO_START
        
    def get_client_addr_list(self):
        addrs = []
        for s in self.__open_sockets:
            addrs.append( self.get_socket_addr(s) )
        return addrs
        
    def get_seq_no(self):
        ''' Just a way to be sure that the correct message has been received '''
        if (self.__seq_no > SBS_MAX_SEQ_NO):
            self.__seq_no = SBS_SERVER_SEQ_NO_START
        self.__seq_no += 2
        return self.__seq_no
        
    def get_socket_addr(self, socket):
        return self.__open_sockets_to_addr[socket]
    
    def get_socket(self, addr):
        return self.__addr_to_open_socket[addr]
    
    def send(self, s, message):
        try:
            s.sendall(message)
        except(socket.error):
            #self.disconnect_socket(s)
            raise SBSNetworkFailure( "Couldn't send data to user at {0}. Broken pipe?".format( self.get_socket_addr(s) ) )
        
    def start(self, host, port):
        self.host = host
        self.port = port
        self.__listen_socket.bind((self.host, self.port))
        self.__listen_socket.listen(5)
        
    def stop(self):
        # MAYBE: send 'bye' to all clients
        try:
            self.__listen_socket.shutdown()
            self.__listen_socket.close()
        except(socket.error):
            pass
        
        for socket in self.__open_sockets:
            try:
                socket.shutdown(socket.SHUT_RDWR)
                socket.close()
            except(socket.error):
                pass
        
    def execute(self, seq_no = None):

        if seq_no == None:
            self.on_execute()
        
        result = None
        
        rlist = [self.__listen_socket] + self.__open_sockets
        for read_socket in rlist:
            if read_socket is self.__listen_socket:
                try:
                    # only allow so and so many connections
                    if len(self.__open_sockets) < SBS_SERVER_MAX_CONNECTIONS:
                            
                        sck, addr = self.__listen_socket.accept()
                        
                        # non-blocking socket
                        sck.setblocking(False)
                        
                        self.__open_sockets.append(sck)
                        
                        self.__open_sockets_to_addr[sck] = addr
                        self.__addr_to_open_socket[addr] = sck
                        
                        self.on_connect( addr )
                    else:
                        self.on_server_full()
                except(socket.error):
                    pass
            else:
                try:
                    data = read_socket.recv(SBS_RECV_BUFFER_SIZE)
                    
                    if data:
                        messages = data.split(SBS_NETWORK_MESSAGE_DELIMITER)[1:]
                        
                        for message in messages:
                            #print "execute: split to message", message
                            message = message.split(SBS_TCPSERVER_DELIMITER)
                            
                            if seq_no == int( message[0] ):
                                result = message
                                continue
                            
                            for message_no in range(2, len(message)):
                                try:
                                    message[message_no] = eval(message[message_no])
                                except(NameError):
                                    pass
                            
                            self.on_message(message, read_socket)
                            
                except(socket.error):
                    pass
                except:
                    print "[Server] Bogus message: '{0}' received from {1}".format(messages, self.get_socket_addr(read_socket))
                    print "[Server] Error details for the message:", sys.exc_info()
                
        return result
                        
    def disconnect_socket(self, s):
        try:
            self.__open_sockets.remove(s)
            self.on_connection_lost( self.get_socket_addr(s) )
            # TODO: Remove the other dictionary values as well
        except(ValueError):
            if self.verbose:
                print '[GameServer] Socket already disconnected'
        
    def on_execute(self):
        raise NotImplemented("Subclass of GameServer must define on_execute.")
    
    def on_message(self, message, s):
        try:
            ret = self._message_handler[ message[1] ]( self.get_socket_addr(s), *message[2:] )
            if ret is not None:       
                ret_msg = SBS_NETWORK_MESSAGE_DELIMITER + \
                    SBS_TCPSERVER_DELIMITER.join(message[:2]) + \
                    SBS_TCPSERVER_DELIMITER + str(ret)
                self.send(s, ret_msg)
            else:
                print "[Warning] Missing return value for function {0} or function is returning None.".format(message[1])
        except:
            raise
            print "[Server] Got an exception (", sys.exc_info(), ") on message \"", \
                message, "\" from addr", self.get_socket_addr(s)
            exception_message = [SBS_INVALID_ID, "py_exception_at_server", sys.exc_info()[:2] ]
            for i in range(len(exception_message)):
                exception_message[i] = str(exception_message[i])
            self.send(s, SBS_NETWORK_MESSAGE_DELIMITER + SBS_TCPSERVER_DELIMITER.join(exception_message))
            #self.send(s, SBS_NETWORK_MESSAGE_DELIMITER + "-1/error/{0}/{1}".format( *sys.exc_info()[:2]))
            
    def on_bye(self, addr):
        print addr, 'said bye. User at {0} is disconnecting.'.format(addr)
        self.disconnect_socket( self.get_socket(addr) )
